package com.lbd.base.service;

import com.lbd.commons.exception.QueryException;
import org.hibernate.SQLQuery;
import org.hibernate.transform.Transformers;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import org.springframework.data.repository.PagingAndSortingRepository;

import javax.persistence.EntityManager;
import javax.persistence.Query;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * Created by WangYajie on 2016/9/13.
 *
 * @param <E>实体类
 * @param <T>    dao层对象
 */
public abstract class BaseServiceImpl<E, T extends PagingAndSortingRepository<E, Serializable> & JpaSpecificationExecutor<E>> implements BaseService<E> {
    @Autowired
    public EntityManager em;

    @Autowired
    public T repository;

    /**
     * 删除对象
     *
     * @param e 对象
     */
    @Override
    public void del(E e) {
        repository.delete(e);
    }

    /**
     * 根据id删除对象
     *
     * @param id id
     */
    @Override
    public void del(Serializable id) {
        repository.delete(id);
    }

    /**
     * 根据id数组,删除对象
     *
     * @param ids id数组
     */
    @Override
    public void del(Serializable ids[]) {
        for (Serializable id : ids) {
            repository.delete(id);
        }
    }

    /**
     * 更新
     *
     * @param e
     */
    @Override
    public void update(E e) {
        repository.save(e);
    }

    /**
     * 保存
     *
     * @param e
     */
    @Override
    public void save(E e) {
        repository.save(e);
    }

    /**
     * 查询所有
     *
     * @return
     */
    @Override
    public List<E> findAll() {
        List<E> list = new ArrayList<>();
        for (E e : repository.findAll()) {
            list.add(e);
        }
        return list;
    }

    /**
     * 按ID查询
     *
     * @param id id
     * @return
     */
    @Override
    public E findById(Serializable id) {
        return repository.findOne(id);
    }

    /**
     * 条件分页查询
     *
     * @param index    页码
     * @param pageSize 每页行数
     * @return
     */
    @Override
    public Page<E> findByPage(int index, int pageSize) {
        PageRequest page = new PageRequest(index, pageSize);
        return repository.findAll(page);
    }

    /**
     * 使用JPA的动态接口，翻页带条件
     *
     * @param index    页码
     * @param pageSize 每页行数
     * @param e        对象
     * @return
     */
    @Override
    public Page<E> findByPageConditionJpa(int index, int pageSize, E e) {
        return this.repository.findAll(getSpecification(e), new PageRequest(index, pageSize));
    }

    /**
     * { return new Specification<E>() {
     *
     * @param e
     * @return
     * @Override public Predicate toPredicate(Root<E> root, CriteriaQuery<?> criteriaQuery, CriteriaBuilder cb) {
     * <p>
     * <p>
     * List<Predicate> list = new ArrayList<>(); list.add(cb.like(root.get("className"), e.toString()));
     * Predicate[] predicates = new Predicate[list.size()]; predicates = list.toArray(predicates); return
     * cb.and(predicates); } };
     */
    public Specification<E> getSpecification(final E e) {
        throw new QueryException();
    }

    /**
     * 分页查询
     *
     * @param index    页码
     * @param pageSize 每页行数
     * @param e
     * @return
     */
    @Override
    public Page<E> findByPageConditionJpql(int index, int pageSize, E e) {
        Map<String, Object> parameter = new HashMap<>();
        String jpql = buildJpql(e, parameter).replaceFirst("and", "where ");
        Query dataQuery = em.createQuery(jpql);
        String countSql;
        if (jpql.trim().startsWith("select")) {
//            select   new  Media(id,createdDate,title,preview ) from
            countSql = jpql.replaceAll("select.+?from", "select count(*) from ");
        } else {
            countSql = "select  count(*)" + jpql;
        }
        Query countQuery = em.createQuery(countSql);
        for (String key : parameter.keySet()) {
            dataQuery.setParameter(key, parameter.get(key));
            countQuery.setParameter(key, parameter.get(key));
        }
        dataQuery.setFirstResult((index - 1) * pageSize);
        dataQuery.setMaxResults(pageSize);
        long totalSize = (long) countQuery.getSingleResult();
        List<E> data = dataQuery.getResultList();
        return new PageImpl<E>(data, new PageRequest(index - 1, pageSize), totalSize);


    }

    /**
     * 分页查询
     *
     * @param index        页码
     * @param pageSize     每页行数
     * @param queryBuilder sql语句
     * @return
     */
    @Override
    public Page<E> findByPageConditionJpql(int index, int pageSize, QueryBuilder queryBuilder) {
        Map<String, Object> parameter = new HashMap<>();
        String jpql = queryBuilder.buildQuery(parameter).replaceFirst("and", "where ");
        Query dataQuery = em.createQuery(jpql);
        String countSql;
        if (jpql.trim().startsWith("select")) {
//            select   new  Media(id,createdDate,title,preview ) from
            countSql = jpql.replaceAll("select.+?from", "select count(*) from ");
        } else {
            countSql = "select  count(*)" + jpql;
        }
        Query countQuery = em.createQuery(countSql);
        for (String key : parameter.keySet()) {
            dataQuery.setParameter(key, parameter.get(key));
            countQuery.setParameter(key, parameter.get(key));
        }
        dataQuery.setFirstResult((index - 1) * pageSize);
        dataQuery.setMaxResults(pageSize);
        long totalSize = (long) countQuery.getSingleResult();
        List<E> data = dataQuery.getResultList();
        return new PageImpl<E>(data, new PageRequest(index - 1, pageSize), totalSize);
    }

    /**
     * 根据jpql查询
     *
     * @param e
     * @return
     */
    @Override
    public List<E> findByConditionJpql(E e) {
        Map<String, Object> parameter = new HashMap<>();
        String jpql = buildExcelJpql(e, parameter).replaceFirst("and", "where ");
        Query dataQuery = em.createQuery(jpql);
        for (String key : parameter.keySet()) {
            dataQuery.setParameter(key, parameter.get(key));
        }
        return dataQuery.getResultList();
    }

    /**
     * @param e
     * @param parameter
     * @return
     */
    public String buildExcelJpql(E e, Map<String, Object> parameter) {
        return buildJpql(e, parameter);
    }

    /**
     * 构建jpql分页 s
     * <p>
     * <p>
     * { StringBuilder jpqlBuilder = new StringBuilder("  from  EventLog t   ");
     * <p>
     * if (e != null) { if (StringUtils.isNotBlank(e.getClassName())) { jpqlBuilder.append(
     * " and  className like :className"); parameter.put("className",e.getClassName()); } } return
     * jpqlBuilder.toString().replaceFirst("and", "where"); }
     *
     * @param e
     * @param parameter
     * @return
     */
    public abstract String buildJpql(E e, Map<String, Object> parameter);

    /**
     * nativeQuery分页
     *
     * @param page      当前页码
     * @param rows      每页的行数
     * @param parameter nativeSql 的参数
     * @param nativeSql nativeSql 字符串
     * @return
     */
    @Override
    public Page findByPageNativeQuery(int page, int rows, Map<String, Object> parameter, String nativeSql) {
        Query nativeQuery = em.createNativeQuery(nativeSql);
        nativeQuery.unwrap(SQLQuery.class).setResultTransformer(Transformers.ALIAS_TO_ENTITY_MAP);
        long totalSize;
        Query countQuery = em.createNativeQuery("select count(*) from (" + nativeSql + ") c");
        for (String key : parameter.keySet()) {
            nativeQuery.setParameter(key, parameter.get(key));
            countQuery.setParameter(key, parameter.get(key));
        }
        if (null == countQuery.getSingleResult()) {
            totalSize = 0;
        } else {
            totalSize = Integer.parseInt(countQuery.getSingleResult().toString());
        }
        nativeQuery.setFirstResult((page - 1) * rows);
        nativeQuery.setMaxResults(rows);
        List data = nativeQuery.getResultList();
        return new PageImpl(data, new PageRequest(page - 1, rows), totalSize);
    }

    /**
     * nativesql返回Map
     *
     * @param parameter nativeSql 的参数
     * @param nativeSql nativeSql 字符串
     * @return
     */
    @Override
    public Map findMapByPageNativeQuery(Map<String, Object> parameter, String nativeSql) {
        Query nativeQuery = em.createNativeQuery(nativeSql);
        nativeQuery.unwrap(SQLQuery.class).setResultTransformer(Transformers.ALIAS_TO_ENTITY_MAP);
        for (String key : parameter.keySet()) {
            nativeQuery.setParameter(key, parameter.get(key));
        }
        return (Map) nativeQuery.getSingleResult();
    }
}
